﻿//
// .hpp
// ~~~~~~~~
//

#ifndef WEBAPP_OAUTH_UTILITY_JSON_PARSER_HPP
#define WEBAPP_OAUTH_UTILITY_JSON_PARSER_HPP

#include "../encoding.hpp"
#include "utility.hpp"

#include <string>
#include <utility>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_as.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/foreach.hpp>

namespace webapp
{
	namespace util
	{
		namespace json
		{
			namespace qi = boost::spirit::qi;
			namespace phx = boost::phoenix;
			namespace standard = boost::spirit::standard;

			struct array;
			struct object;
			typedef
				boost::variant<
					  boost::recursive_wrapper<array>
					, boost::recursive_wrapper<object>
					, std::string
				>
			node_value_type;

			struct node_value
			{
				node_value_type value;
			};

			struct array
			{
				//typedef std::vector<node_value> type;
				std::vector<node_value> children;
			};

			//std::pairの変わり・・・
			struct pair
			{
				std::string		key;
				node_value		node;
			};

			struct object
			{
				//typedef std::vector<pair> type;
				std::vector<pair> children;
			};
		}
	}
}
BOOST_FUSION_ADAPT_STRUCT(
	 webapp::util::json::node_value,
	(webapp::util::json::node_value_type,		value)
)

BOOST_FUSION_ADAPT_STRUCT(
	 webapp::util::json::array,
	(std::vector<webapp::util::json::node_value>/*webapp::util::json::array::type*/,		children)
)

BOOST_FUSION_ADAPT_STRUCT(
	 webapp::util::json::object,
    (std::vector<webapp::util::json::pair>/*webapp::util::json::object::type*/,		children)
)

BOOST_FUSION_ADAPT_STRUCT(
	 webapp::util::json::pair,
    (std::string,						key)
	(webapp::util::json::node_value,	node)
)

namespace webapp
{
	namespace util
	{
		namespace json
		{
			//template<typename Iterator>
			class parser
				: public qi::grammar<std::string::const_iterator, node_value(), standard::space_type>
			{
				typedef std::string					string_type;
				typedef string_type::const_iterator	iterator_type;
			public:
				parser()
					: parser::base_type(entry_)
				{
					using qi::lit;
					using qi::lexeme;

					entry_ %=
							object_[phx::at_c<0>(qi::_val) = phx::static_cast_<node_value_type>( qi::_1 )]
						|	array_[phx::at_c<0>(qi::_val) = phx::static_cast_<node_value_type>( qi::_1 )];

					object_ %=
							lit('{') >> -members_[qi::_val = qi::_1] >> '}';

					members_ %=
						( pair_ % lit(',') )[phx::at_c<0>(qi::_val) = qi::_1];

					pair_ %=
							string_ >> ':' >> value_;

					array_ %=
							lit('[') >> -elements_[qi::_val = qi::_1] >> ']';

					elements_ %=
						( value_ % lit(",") )[phx::at_c<0>(qi::_val) = qi::_1];

					value_ %=
						(
							string_//[phx::at_c<0>( qi::_val ) = qi::_1]
						|	number_//[phx::at_c<0>( qi::_val ) = qi::_1]
						|	object_//[phx::at_c<0>( qi::_val ) = qi::_1]
						|	array_//[phx::at_c<0>( qi::_val ) = qi::_1]
						|	qi::lit("true")[phx::at_c<0>( qi::_val ) = ""]
						|	qi::lit("false")[phx::at_c<0>( qi::_val ) = ""]
						|	qi::lit("null")[phx::at_c<0>( qi::_val ) = ""]
						);

					//
					number_ %=
						qi::double_[qi::_val = phx::bind(boost::lexical_cast<string_type, double>, qi::_1)];

					string_ %=
							qi::eps[qi::_a = ""]
						>>	lexeme[
									lit('\"')
								>>	*(
										escape_character_ | character_
									)[qi::_a += qi::_1]
								>>	lit('\"')
							]
						>> qi::eps[qi::_val = qi::_a];

					character_ %=
						qi::char_ - '\"';

					escape_character_ %=
						(
							lit("\\\"")	[qi::_val = '\"']
						|	lit("\\\\")	[qi::_val = '\\']
						|	lit("\\n")	[qi::_val = '\n']
						|	lit("\\t")	[qi::_val = '\t']
						|	lit("\\r")	[qi::_val = '\r']
						|	lit("\\/")	[qi::_val = '/']
						|	(
								lit("\\u") >> unicode_charactor_
							)
						);

					unicode_charactor_ %=
							qi::eps[qi::_a = ""]
						>>	qi::repeat(4)[ standard::xdigit[qi::_a += qi::_1] ]
						>>	qi::eps[qi::_val = phx::bind( babel::unicode_to_sjis, phx::construct<std::wstring>( 1, phx::static_cast_<wchar_t>( phx::bind( hex_to_dec, qi::_a ) ) ) )];
				}

			private:
				qi::rule<iterator_type, node_value(), standard::space_type>
					entry_;
				qi::rule<iterator_type, object(), standard::space_type>
					object_;
				qi::rule<iterator_type, object(), standard::space_type>
					members_;
				qi::rule<iterator_type, pair(), standard::space_type>
					pair_;
				qi::rule<iterator_type, array(), standard::space_type>
					array_;
				qi::rule<iterator_type, array(), standard::space_type>
					elements_;
				qi::rule<iterator_type, node_value(), standard::space_type>
					value_;

				qi::rule<iterator_type, string_type(), standard::space_type>
					number_;

				qi::rule<iterator_type, qi::locals<string_type>, string_type(), standard::space_type>
					string_;
				qi::rule<iterator_type, string_type()>
					character_, escape_character_;
				qi::rule<iterator_type, qi::locals<string_type>, string_type()>
					unicode_charactor_;
			};

		} // - json
	} // - util
} // - webapp

#endif /*WEBAPP_OAUTH_UTILITY_JSON_PARSER_HPP*/
